package com.ys168.gam.shortestpath.view;

import static com.ys168.gam.shortestpath.uimessage.UIMessageUtil.getString;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JPanel;

import com.ys168.gam.shortestpath.model.Node;
import com.ys168.gam.shortestpath.uimessage.UIMessageCode;
import com.ys168.gam.shortestpath.util.DrawALHelper;
import com.ys168.gam.shortestpath.util.MoveHelper;

/**
 *
 * @author Jason Krebs
 * @date 2015年2月2日
 */
public class CanvaPanel extends JPanel {

    public interface IRunningFinishedListener {
        void onRunningFinished();
    }

    private static final long serialVersionUID = 6306635296207278333L;

    private static final Logger log = Logger.getLogger(CanvaPanel.class.getName());
    private List<Node> nodes;
    private Node focusedNode;
    private Node selectedNode;
    private Node player;

    private Point2D tempPoint;
    private boolean isDirectedGraph;
    private boolean isShowLineLength;

    private boolean isRunning;

    private IRunningFinishedListener runningFinishedListener;

    public CanvaPanel() {
        isShowLineLength = true;
        isDirectedGraph = false;
        nodes = new ArrayList<Node>();
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (tempPoint != null) {
                    tempPoint = null;
                }
            }
        });
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                setFocused(e.getPoint());
            }
        });
    }

    /**
     * 添加节点
     *
     * @param node
     */
    public void addNode(Node node) {
        if (!nodes.contains(node)) {
            node.setIndex(getNextNodeIndex());
            nodes.add(node);
            repaint();
        }
    }

    /**
     * 清空所有节点
     */
    public void clearAllNodes() {
        focusedNode = null;
        selectedNode = null;
        player = null;
        nodes.clear();
        if (isVisible()) {
            repaint();
        }
    }

    /**
     * 清除最后添加的直线
     */
    public boolean clearLine() {
        Node node = getSelectedNode();

        if (node == null) {
            return false;
        }
        for (Node in : node.getInNodes()) {
            in.removeNode(node);
        }
        for (Node out : node.getOutNodes()) {
            out.removeNode(node);
        }
        node.clearAllNodes();
        repaint();

        return true;
    }

    /**
     * 生成直线
     *
     * @param point
     * @return
     */
    public boolean generateLine(Point2D point) {
        if (getFocusedNode() == null) {
            return false;
        }
        Node node = getNodeByPosition(point);
        if (node == null || getFocusedNode().equals(node)) {
            tempPoint = point;
            repaint();
            return false;
        }

        if (getFocusedNode() != null && node != null) {
            node.addInNode(getFocusedNode());
            getFocusedNode().addOutNode(node);
            setFocused(null);
            tempPoint = null;

            repaint();

            return true;
        }
        return false;
    }

    public List<Node> getAllNodes() {
        return Collections.unmodifiableList(nodes);
    }

    /**
     * 获取聚焦的节点
     *
     * @return
     */
    public Node getFocusedNode() {
        return focusedNode;
    }

    /**
     * 获取直线数
     *
     * @return
     */
    public int getLineCount() {
        int count = 0;
        for (Node node : nodes) {
            count += node.getOutNodes().size();
        }
        return count;
    }

    /**
     * 根据位置获取节点，如果没找到则返回null
     *
     * @param point
     * @return
     */
    public Node getNodeByPosition(Point2D point) {
        for (Node node : nodes) {
            if (node.isInclude(point)) {
                return node;
            }
        }
        return null;
    }

    /**
     * 获取节点数
     *
     * @return
     */
    public int getNodeCount() {
        return nodes.size();
    }

    /**
     * 获取选择的节点
     *
     * @return
     */
    public Node getSelectedNode() {
        return selectedNode;
    }

    /**
     * 获取起始节点
     *
     * @return
     */
    public Node getSource() {
        if (!isRunnable()) {
            throw new RuntimeException(getString(UIMessageCode.LOG_01));
        }
        Point2D point = player.getLocation();
        return getNodeByPosition(point);
    }

    // @formatter:off
    public boolean isRunnable() {
        return !isRunning && player != null && player.isVisible() && getSelectedNode() != null
                && getSelectedNode().getIndex() != player.getIndex() && getLineCount() > 0;
    }

    public boolean isShowLineLength() {
        return isShowLineLength;
    }

    public void loadNodes(List<Node> nodes) {
        this.nodes.clear();
        this.nodes.addAll(nodes);
        repaint();
    }

    /**
     * 移动节点
     *
     * @param point
     * @return
     */
    public void moveNode(Point2D point) {
        if (getFocusedNode() != null) {
            getFocusedNode().setLocation(point.getX(), point.getY());
            if (player != null && getFocusedNode().getIndex() == player.getIndex()) {
                player.setLocation(point);
            }
            repaint();
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        drawNode((Graphics2D) g);
        drawLine((Graphics2D) g);
        drawPlayer((Graphics2D) g);
    }

    // @formatter:on

    /**
     * 移除节点
     *
     * @param point
     * @return
     */
    public boolean removeNode(Point2D point) {
        Node node = getNodeByPosition(point);
        if (node == null) {
            return false;
        }

        for (Node n : node.getAllNodes()) {
            n.removeNode(node);
        }
        nodes.remove(node);
        setSelectedNode((Node) null);
        repaint();

        return true;
    }

    /**
     * 创建人物
     *
     * @param image
     */
    public void resetRunner(Image image) {
        if (nodes.isEmpty()) {
            return;
        }
        if (player == null) {
            Node node = nodes.get(0);
            player = new Node(node.getLocation(), image);
            player.setIndex(node.getIndex());
        } else {
            player.setLocation(nodes.get(0).getLocation());
            player.setIndex(nodes.get(0).getIndex());
        }
        repaint();
    }

    public void run(final List<Node> path) {
        isRunning = true;

        if (path.isEmpty()) {
            log.log(Level.WARNING, getString(UIMessageCode.LOG_02));
            stop();
            return;
        }
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (Node node : path) {
                    if (!isRunning) {
                        break;
                    }
                    player.setIndex(node.getIndex());
                    Point2D source = player.getLocation();
                    Point2D target = node.getLocation();
                    while (!node.getLocation().equals(player.getLocation())) {
                        Point2D nextLocation = MoveHelper.getNextLocation(player.getLocation(), source, target);
                        player.setLocation(nextLocation);
                        repaint();
                        try {
                            Thread.sleep(17);
                        } catch (InterruptedException e) {
                            log.log(Level.WARNING, e.getMessage());
                        }
                    }
                }
                stop();
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    public void setDirectedGraph(boolean isDirectedGraph) {
        this.isDirectedGraph = isDirectedGraph;
    }

    /**
     * 设置聚焦节点
     *
     * @param point
     * @return
     */
    public Node setFocused(Point2D point) {
        Node node = (point == null) ? null : getNodeByPosition(point);
        if (node != focusedNode) {
            focusedNode = node;
            repaint();
        }
        return focusedNode;
    }

    public void setOnRunningFinshed(IRunningFinishedListener runningFinishedListener) {
        this.runningFinishedListener = runningFinishedListener;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    /**
     * 设置选择的节点
     *
     * @param selectedNode
     */
    public void setSelectedNode(Node selectedNode) {
        this.selectedNode = (this.selectedNode == selectedNode) ? null : selectedNode; // 再次选择时，取消选择
        repaint();
    }

    /**
     * 设置选择的节点
     *
     * @param point
     * @return
     */
    public Node setSelectedNode(Point2D point) {
        Node node = getNodeByPosition(point);
        setSelectedNode(node);
        return node;
    }

    public void setShowLineLength(boolean isShowLineLength) {
        this.isShowLineLength = isShowLineLength;
    }

    /**
     * 停止当前正在运行的
     */
    public void stop() {
        if (isRunning) {
            isRunning = false;
            if (runningFinishedListener != null) {
                runningFinishedListener.onRunningFinished();
            }
        }
    }

    private void drawLine(Graphics2D g2) {
        g2.setColor(Color.GREEN);
        for (Node node : nodes) {
            if (!node.isVisible()) {
                continue;
            }
            for (Node endNode : node.getOutNodes()) {
                if (endNode.isVisible()) {
                    Point2D a = node.getLocation();
                    Point2D b = endNode.getLocation();
                    drawLine(a, b, g2);
                    if (isShowLineLength) {
                        g2.drawString(String.format("%.2f", node.calculateLength(endNode)),
                                (float) (a.getX() + b.getX()) / 2, (float) (a.getY() + b.getY()) / 2);

                    }
                }
            }
        }
        if (tempPoint != null && getFocusedNode() != null) {
            g2.setColor(Color.RED);
            drawLine(getFocusedNode().getLocation(), tempPoint, g2);
        }
    }

    private void drawLine(Point2D a, Point2D b, Graphics2D g2) {
        if (isDirectedGraph) {
            DrawALHelper.drawAL(a, b, g2);
        } else {
            g2.draw(new Line2D.Double(a, b));
        }
    }

    private void drawNode(Graphics2D g2d) {
        for (Node node : nodes) {
            g2d.setColor(Color.WHITE);
            node.draw(g2d);
            if (node.equals(selectedNode)) {
                g2d.setColor(Color.YELLOW);
                g2d.draw(node.getShape());
            } else if (node.equals(focusedNode)) {
                g2d.setColor(Color.WHITE);
                g2d.draw(node.getShape());
            }
        }
    }

    private void drawPlayer(Graphics2D g2d) {
        if (player != null && player.isVisible()) {
            player.draw(g2d);
        }
    }

    /**
     * 生成下一个节点的标识ID
     *
     * @return
     */
    private int getNextNodeIndex() {
        return nodes.isEmpty() ? 1 : nodes.get(nodes.size() - 1).getIndex() + 1;
    }

}
